<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
  <link rel="stylesheet" href="test.css">
  <title>Zepto Ajax unit tests</title>
  <script src="../vendor/evidence.js"></script>
  <script src="evidence_runner.js"></script>
  <script>
    // avoid caching
    (function(){
      function load(scripts){
        scripts.split(' ').forEach(function(script){
          document.write('<script src="../src/'+script+'.js?'+(+new Date)+'"></scr'+'ipt>')
        })
      }

      load('zepto event ajax ie')
    })()
  </script>
</head>
<body>
  <h1>Zepto Ajax unit tests</h1>
  <p id="results">
    Running… see browser console for results
  </p>
  <div id="fixtures">
    <div id="ajax_load"></div>
  </div>

  <script>
  (function(){

    function deferredResume(t, fn) {
      setTimeout(function() { t.resume(fn || function(){}) }, 5)
    }

    function resumeOnAjaxError(t) {
      $(document).on('ajaxError', function(e, xhr){
        deferredResume(t, function(){
          t.assert(false, "request errored out: " + xhr.responseText)
        })
      })
    }

    function toArray(list) {
      if ($.type(list) == "string") return list.split(/(?:\s*,\s*|\s+)/)
      else return list
    }

    Evidence.Assertions.assertEqualList = function(expected, actual, message) {
      var expectedList = toArray(expected),
          actualList   = toArray(actual)

      this._assertExpression(
        expectedList.join(' ') == actualList.join(' '),
        message || "Lists don't match.",
        'Expected %o, got %o.', expectedList, actualList
      )
    }

    Evidence.Assertions.assertLine = function(line, string, message) {
      var lines = string.split("\n")

      this._assertExpression(
        $.inArray(line, lines) >= 0,
        message || "Line not found.",
        'Expected line %s in %s.', line, string
      )
    }

    Evidence.Assertions.assertLinePattern = function(pattern, string, message) {
      var lines = string.split("\n")

      var i, match = false
      if (lines.length) {
        for (i = 0; i < lines.length; i++) {
          if (lines[i].search(pattern) !== -1) {
            match = true
            break
          }
        }
      }

      this._assertExpression(
        match === true,
        message || "Pattern not found.",
        'Expected pattern %s in %s.', pattern, string
      )
    }

    var slice = [].slice

    function CallbackRegistry(t) {
      this.testCase = t
      this.called = []
    }
    $.extend(CallbackRegistry.prototype, {
      register: function(name, context, args){
        this.called.push([name, context, args])
      },
      handler: function(name, fn){
        var that = this
        return function(){
          that.register(name, this, slice.call(arguments))
          if (fn) return fn.apply(this, arguments)
        }
      },
      resumeHandler: function(name, fn){
        var that = this
        return this.handler(name, function(){
          var context = this, args = arguments
          setTimeout(function(){
            that.testCase.resume(function(){
              if (fn) fn.apply(context, args)
            })
          }, 5)
        })
      },
      handlers: function(){
        var that = this, hash = {}
        $.each(arguments, function(i, name){
          hash[name] = that.handler(name)
        })
        return hash
      },
      events: function(){
        return $.map(this.called, function(item){ return item[0] })
      },
      context: function(name){
        return this.find(name, function(item){ return item[1] })
      },
      args: function(name){
        return this.find(name, function(item){ return item[2] })
      },
      target: function(name){
        var args = this.args(name)
        return args && args[0].target
      },
      find: function(name, fn){
        var obj
        $.each(this.called, function(i, item){
          if (item[0] == name) {
            obj = fn(item)
            return false
          }
        })
        return obj
      }
    })

    Evidence('ZeptoAjaxTest', {

      setUp: function() {
        var reg = new CallbackRegistry(this),
            globals = toArray('ajaxBeforeSend ajaxSend ajaxSuccess ajaxError ajaxComplete')

        $(document).on(reg.handlers.apply(reg, globals))
        this.reg = reg
        window.jsonpDummy = $.noop
      },

      tearDown: function() {
        $(document).off()
        for (var key in window)
          if (/^(Zepto\d+|blah)$/.test(key) && window[key] === undefined)
            delete window[key]
      },

      testAjaxBase: function(t){
        t.pause()
        var xhr = $.ajax({
          url: 'fixtures/ajax_load_simple.html',
          success:  t.reg.handler('success'),
          error:    t.reg.handler('error'),
          complete: t.reg.resumeHandler('complete', function(){
            t.assertEqualList('ajaxBeforeSend ajaxSend success ajaxSuccess complete ajaxComplete', t.reg.events())
          })
        })
        t.assert($.isFunction(xhr.getResponseHeader))
      },

      testAjaxGet: function(t){
        t.pause()
        var xhr = $.get('echo', t.reg.resumeHandler('success', function(response){
          t.assertIdentical(window, this)
          t.assertLine("GET ?{}", response)
          t.assertEqualList('ajaxBeforeSend ajaxSend success ajaxSuccess ajaxComplete', t.reg.events())
        }))
        t.assertIn('abort', xhr)
      },

      testAjaxGetWithParams: function(t){
        t.pause()
        $.get('echo', { sample: 'data' }, t.reg.resumeHandler('success', function(response){
          t.assertLine('GET ?{"sample":"data"}', response)
          t.assertLine("accept: */*", response)
        }))
      },

      testAjaxGetWithParamsAndType: function(t){
        t.pause()
        $.get('echo', { sample: 'plain' }, t.reg.resumeHandler('success', function(response){
          t.assertLine('GET ?{"sample":"plain"}', response)
          t.assertLine("accept: text/plain", response)
        }), 'text')
      },

      testAjaxGetWithParamsAndTypeNoCallback: function(t){
        t.pause()
        $(document).on('ajaxSuccess', function(e, xhr, settings, response){
          deferredResume(t, function(){
            t.assertLine('GET ?{"sample":"plain"}', response)
            t.assertLine("accept: text/plain", response)
            t.assertEqualList('ajaxBeforeSend ajaxSend ajaxSuccess ajaxComplete', t.reg.events())
          })
        })
        $.get('echo', { sample: 'plain' }, 'text')
      },

      testAjaxGetWithParamsAutoParsesJSON: function(t) {
        t.pause()
        $.get('json', { sample: 'data' }, t.reg.resumeHandler('success', function(data){
          t.assertEqual('data', data.query.sample)
          t.assertEqual('world', data.hello)
        }))
      },

      testAjaxGetNoParamsAutoParsesJSON: function(t) {
        t.pause()
        $.get('json', t.reg.resumeHandler('success', function(data){
          t.assertIdentical(0, $.map(data.query).length)
          t.assertEqual('world', data.hello)
        }))
      },

      testAjaxGetNullParamsAutoParsesJSON: function(t) {
        t.pause()
        $.get('json', null, t.reg.resumeHandler('success', function(data){
          t.assertIdentical(0, $.map(data.query).length)
          t.assertEqual('world', data.hello)
        }))
      },

      testAjaxBeforeSendSetRequestHeader: function(t){
        t.pause()
        $.ajax({
          url: 'echo',
          beforeSend: function(xhr, settings){
            xhr.setRequestHeader('Accept', 'text/plain')
            t.assertUndefined(settings.headers)
          },
          success: t.reg.resumeHandler('success', function(response){
            t.assertLine("accept: text/plain", response)
          })
        })
      },

      testAjaxPost: function(t){
        t.pause()
        var xhr = $.post('echo', t.reg.resumeHandler('success', function(response){
          t.assertIdentical(window, this)
          t.assertLine("POST ?{}", response)
          t.assertLine("accept: */*", response)
          t.assertLine('{}', response)
          t.assertEqualList('ajaxBeforeSend ajaxSend success ajaxSuccess ajaxComplete', t.reg.events())
        }))
        t.assertIn('abort', xhr)
      },

      testAjaxPostWithPayloadAndType: function(t) {
        t.pause()
        var payload = { sample: 'data' }
        $.post('echo', payload, t.reg.resumeHandler('success', function(response){
          t.assertLine("content-type: application/x-www-form-urlencoded", response)
          t.assertLine("accept: text/plain", response)
          t.assertLine('{"sample":"data"}', response)
        }), 'text')
      },

      testAjaxPostAutoParsesJSON: function(t) {
        t.pause()
        var payload = { sample: 'data' }
        $.post('create', payload, t.reg.resumeHandler('success', function(data){
          t.assertEqual('data', data.payload.sample)
        }))
      },

      testNumberOfActiveRequests: function(t) {
        var maxActive = 0, ajaxStarted = 0, ajaxEnded = 0, requestsCompleted = 0
        t.assertIdentical(0, $.active, 'initial count mismatch')
        $(document)
          .on('ajaxStart', function() { ajaxStarted++ })
          .on('ajaxEnd', function() { ajaxEnded++ })
          .on('ajaxSend', function() {
            if ($.active > maxActive) maxActive = $.active
          })
          .on('ajaxComplete', function() {
            if (++requestsCompleted == 3)
              deferredResume(t, function() {
                this.assertEqual(3, maxActive)
                this.assertIdentical(0, $.active)
              })
          })

        t.pause()
        $.ajax({ url: 'echo' })
        $.ajax({ url: 'echo' })
        $.ajax({ url: 'echo' })
      },

      testAjaxJSON: function(t){
        t.pause()
        resumeOnAjaxError(t)

        $.ajax({
          url: 'json',
          headers: { accept: 'application/json' },
          success: t.reg.resumeHandler('success', function(data){
            t.assertEqual('world', data.hello)
          })
        })
      },

      testAjaxDataFilterJSON: function(t){
        t.pause()
        resumeOnAjaxError(t)

        $.ajax({
          url: 'taintedJSON',
          dataFilter: function(data, type) {
            if (!data) return
            return data.replace(/^\s*while\(1\);\s*/, '')
          },
          headers: { accept: 'application/json' },
          success: t.reg.resumeHandler('success', function(data){
            t.assertEqual('world', data.hello)
          })
        })
      },

      testAjaxGetJSON: function(t){
        t.pause()
        resumeOnAjaxError(t)

        var xhr = $.getJSON(
          'json',
          t.reg.resumeHandler('success', function(data){
            t.assertEqual('world', data.hello)
            t.assert($.isEmptyObject(data.query))
            t.assertEqualList('ajaxBeforeSend ajaxSend success ajaxSuccess ajaxComplete', t.reg.events())
          })
        )
        t.assertIn('abort', xhr)
      },

      testAjaxGetJSONWithParams: function(t){
        t.pause()
        resumeOnAjaxError(t)

        $.getJSON(
          'json', { sample: 'data' },
          t.reg.resumeHandler('success', function(data){
            t.assertEqual('data', data.query.sample)
          }),
          'text' // ignored
        )
      },

      testAjaxJSONParserError: function(t){
        t.pause()
        $.ajax({
          url:      'json',
          dataType: 'json',
          data:     { invalid: true },
          success:  t.reg.handler('success'),
          error:    t.reg.handler('error'),
          complete: t.reg.resumeHandler('complete', function(){
            t.assertEqualList('ajaxBeforeSend ajaxSend error ajaxError complete ajaxComplete', t.reg.events())
            var errorArgs = t.reg.args('error')
            t.assertEqual('parsererror', errorArgs[1])
            t.assert(errorArgs[2] instanceof SyntaxError)
            var globalErrorArgs = t.reg.args('ajaxError')
            t.assert(globalErrorArgs[3] instanceof SyntaxError)
            t.assertEqual(0, $('script[src^=slow]').size())
          })
        })
      },

      testAjaxEqualQuestionmarkIsNotAlwaysJSONP: function(t){
        t.pause()
        resumeOnAjaxError(t)
        $.ajax({
          url: 'echo=?',
          dataType: 'text',
          success: t.reg.resumeHandler('success', function(data){
            t.assertLine('GET ?{}', data)
            t.assertLine("accept: text/plain", data)
          })
        })
      },

      testAjaxGetJSONP: function(t){
        t.pause()
        $.getJSON(
          'jsonp?callback=?&timestamp='+(+new Date),
          t.reg.resumeHandler('success', function(data){
            t.assertEqual('world', data.hello)
            t.assertEqualList('ajaxBeforeSend ajaxSend success ajaxSuccess ajaxComplete', t.reg.events())
            t.assertEqual(0, $('script[src^=jsonp]').size())
          })
        )
        t.assertEqual(1, $('script[src^=jsonp]').size())
      },

      testAjaxGetJSONPWithParams: function(t){
        t.pause()
        $.getJSON(
          'jsonp?callback=?&timestamp='+(+new Date),
          { sample: 'data' },
          t.reg.resumeHandler('success', function(data){
            t.assertEqual('data', data.query.sample)
          })
        )
      },

      testAjaxJSONP: function(t){
        t.pause()
        var xhr = $.ajaxJSONP({
          url: 'jsonp?callback=?&timestamp='+(+new Date),
          success:  t.reg.handler('success'),
          complete: t.reg.resumeHandler('complete', function(data){
            var successArgs = t.reg.args('success')
            t.assertEqual('world', successArgs[0].hello)
            t.assertEqualList('ajaxBeforeSend ajaxSend success ajaxSuccess complete ajaxComplete', t.reg.events())
            t.assertEqual(0, $('script[src^=jsonp]').size())
          })
        })
        t.assertIn('abort', xhr)
      },

      testAjaxJSONPBeforeSend: function(t){
        t.pause()
        $(document).on('ajaxBeforeSend', function(){ return false })

        $.ajax({
          url: 'jsonp?callback=?&timestamp='+(+new Date),
          beforeSend: t.reg.resumeHandler('beforeSend', function(){
            t.assertEqualList('beforeSend ajaxBeforeSend ajaxError complete ajaxComplete', t.reg.events())
          }),
          success:  t.reg.handler('success'),
          complete: t.reg.handler('complete')
        })
      },

      testAjaxGetJSONPErrorCallback: function(t){
        t.pause()
        $.ajax({
          type:     'GET',
          url:      'fixtures/404.js?timestamp='+(+new Date),
          dataType: 'jsonp',
          success:  t.reg.handler('success'),
          error:    t.reg.handler('error'),
          complete: t.reg.resumeHandler('complete', function(){
            t.assertEqualList('ajaxBeforeSend ajaxSend error ajaxError complete ajaxComplete', t.reg.events())
            t.assertEqual(0, $('script[src^=fixtures]').size())
          })
        })
      },

      testAjaxGetJSONPErrorWithoutCallback: function(t){
        t.pause()
        $.ajax({
          type:     'GET',
          url:      'fixtures/404.js?timestamp='+(+new Date),
          dataType: 'jsonp',
          success:  t.reg.handler('success'),
          complete: t.reg.resumeHandler('complete', function(){
            t.assertEqualList('ajaxBeforeSend ajaxSend ajaxError complete ajaxComplete', t.reg.events())
            t.assertEqual(0, $('script[src^=fixtures]').size())
          })
        })
      },

      testAjaxJSONPTimeout: function(t){
        t.pause()
        $.ajax({
          url:      'slow?timestamp='+(+new Date),
          dataType: 'jsonp',
          jsonpCallback: 'jsonpDummy',
          timeout:  20,
          success:  t.reg.handler('success'),
          error:    t.reg.handler('error'),
          complete: t.reg.resumeHandler('complete', function(){
            t.assertEqualList('ajaxBeforeSend ajaxSend error ajaxError complete ajaxComplete', t.reg.events())
            var errorArgs = t.reg.args('error')
            t.assertEqual('timeout', errorArgs[1])
            var globalErrorArgs = t.reg.args('ajaxError')
            t.assertEqual('timeout', globalErrorArgs[3])
            t.assertEqual(0, $('script[src^=slow]').size())
          })
        })
      },

      testAjaxJSONPAbort: function(t){
        t.pause()
        var xhr = $.ajax({
          url:      'slow?timestamp='+(+new Date),
          dataType: 'jsonp',
          jsonpCallback: 'jsonpDummy',
          timeout:  30,
          success:  t.reg.handler('success'),
          error:    t.reg.handler('error'),
          complete: t.reg.handler('complete')
        })
        setTimeout(function(){ xhr.abort() }, 10)

        setTimeout(function(){
          t.resume(function(){
            t.assertEqualList('ajaxBeforeSend ajaxSend error ajaxError complete ajaxComplete', t.reg.events())
            var errorArgs = t.reg.args('error')
            t.assertEqual('abort', errorArgs[1])
            var globalErrorArgs = t.reg.args('ajaxError')
            t.assertEqual('abort', globalErrorArgs[3])
            t.assertEqual(0, $('script[src^=slow]').size())
          })
        }, 50)
      },

      testAjaxJSONPError: function(t){
        t.pause()
        var xhr = $.ajax({
          url:      'error?timestamp='+(+new Date),
          dataType: 'jsonp',
          success:  t.reg.handler('success'),
          error:    t.reg.handler('error'),
          complete: t.reg.resumeHandler('complete', function(){
            t.assertEqualList('ajaxBeforeSend ajaxSend error ajaxError complete ajaxComplete', t.reg.events())
            var errorArgs = t.reg.args('error')
            t.assertEqual('error', errorArgs[1])
            t.assertEqual(0, $('script[src^=error]').size())
          })
        })
      },

      testAjaxJSONPWithDataSupport: function(t) {
        t.pause()
        var xhr = $.ajax({
          url: 'jsonp?callback=?&timestamp='+(+new Date),
          data: {
            param1: 'val1',
            param2: 'val2'
          },
          success: t.reg.resumeHandler('success', function(data){
            t.assertEqual('val1', data.query.param1)
            t.assertEqual('val2', data.query.param2)
          })
        })
      },

      testAjaxJSONPWithCallbackName: function(t) {
        var xhr = $.ajax({
          url: 'jsonp',
          dataType: 'jsonp',
          jsonpCallback: 'blah',
          success: t.reg.resumeHandler('success', function(data){
            t.assertUndefined(window.blah)
          })
        })

        t.assert($.isFunction(window.blah))
        t.pause()
      },

      testAjaxJSONPWithCallbackNameAsFunction: function(t) {
        var xhr = $.ajax({
          url: 'jsonp',
          dataType: 'jsonp',
          jsonpCallback: function(){ return 'blah' },
          success: t.reg.resumeHandler('success', function(data){
            t.assertUndefined(window.blah)
          })
        })

        t.assert($.isFunction(window.blah))
        t.pause()
      },

      testAjaxJSONPWithCallbackNameAndJSONPsetToFalse: function(t) {
        var xhr = $.ajax({
          url: 'jsonpBlah',
          dataType: 'jsonp',
          jsonp: false,
          jsonpCallback: 'blah',
          success: t.reg.resumeHandler('success', function(data){
            t.assertUndefined(window.blah)
          })
        })

        var scriptSrc = $('head script:last-child').attr('src')
        t.assert(/^jsonpBlah\?_=[\d]{13}$/.test(scriptSrc), scriptSrc)
        t.assert($.isFunction(window.blah))
        t.pause()
      },

      testAjaxJSONPWithCallbackNameAsFunctionAndJSONPsetToFalse: function(t) {
        var xhr = $.ajax({
          url: 'jsonpBlah',
          dataType: 'jsonp',
          jsonp: false,
          jsonpCallback: function(){ return 'blah' },
          success: t.reg.resumeHandler('success', function(data){
            t.assertUndefined(window.blah)
          })
        })

        var scriptSrc = $('head script:last-child').attr('src')
        t.assert(/^jsonpBlah\?_=[\d]{13}$/.test(scriptSrc), scriptSrc)
        t.assert($.isFunction(window.blah))
        t.pause()
      },

      testAjaxJSONPWithData: function(t) {
        t.pause()
        $.ajax({
          url: 'jsonp',
          type: 'post',
          data: { foo: 'bar' },
          dataType: 'jsonp',
          success: t.reg.resumeHandler('success', function(data){
            t.assertEqual('bar', data.query.foo)
          })
        })
      },

      testNoCacheParam: function(t) {
        t.pause()
        var xhr = $.ajax({
          url:'echo',
          cache: false,
          success: t.reg.resumeHandler('success', function(response){
            // check that the no-cache param (or an element of it) looks like a timestamp
            t.assertLinePattern(/"_":(\[".*",)?"[\d]{13}"/g, response)
          })
        })
      },

      testNoCacheParameterWithParam: function(t) {
        t.pause()
        var xhr = $.ajax({
          url:'echo',
          data:{ data: 'sample' },
          cache: false,
          success: t.reg.resumeHandler('success', function(response){
            // check that the no-cache param (or an element of it) looks like a timestamp
            t.assertLinePattern(/"_":(\[".*",)?"[\d]{13}"/g, response)
          })
        })
      },

      testNoCacheParameterAlreadyPresent: function(t) {
        t.pause()
        var xhr = $.ajax({
          url:'echo',
          data:{ _: 'test' },
          cache: false,
          success: t.reg.resumeHandler('success', function(response){
            // check that the no-cache param (or an element of it) looks like a timestamp
            t.assertLinePattern(/"_":(\[".*",)?"[\d]{13}"/g, response)
          })
        })
      },

      testNoCacheParameterAlreadyPresentWithParam: function(t) {
        t.pause()
        var xhr = $.ajax({
          url:'echo',
          data:{ _: 'test', data: 'sample' },
          cache: false,
          success: t.reg.resumeHandler('success', function(response){
            // check that the no-cache param (or an element of it) looks like a timestamp
            t.assertLinePattern(/"_":(\[".*",)?"[\d]{13}"/g, response)
          })
        })
      },

      testJSONPCacheBustingByDefault: function(t) {
        $.ajax({
          url: 'jsonp?callback=?',
          success: t.reg.resumeHandler('success')
        })

        var scriptSrc = $('head script:last-child').attr('src')
        t.assert(/^jsonp\?callback=Zepto\d+&_=[\d]{13}$/.test(scriptSrc), scriptSrc)
        t.pause()
      },

      testJSONPCacheBustingDisabled: function(t) {
        $.ajax({
          url: 'jsonp?callback=?',
          cache: true,
          success: t.reg.resumeHandler('success')
        })

        var scriptSrc = $('head script:last-child').attr('src')
        t.assert(/^jsonp\?callback=Zepto\d+$/.test(scriptSrc), scriptSrc)
        t.pause()
      },

      testBrowserCaching: function(t) {
        t.pause()
        resumeOnAjaxError(t)
        $.ajax({
          url: 'cached',
          cache: true,
          success: function(firstResponse){
            $.ajax({
              url: 'cached',
              cache: true,
              success: t.reg.resumeHandler('success', function(secondResponse){
                t.assertEqual(secondResponse, firstResponse)
              })
            })
          }
        })
      },

      testAjaxLoad: function(t) {
        t.pause()
        var testEl = $('#ajax_load')
        var el = testEl.load(
          'fixtures/ajax_load_simple.html',
          t.reg.resumeHandler('success', function(response, status, xhr){
            t.assertIdentical(testEl, this)
            t.assertEqual("simple ajax load\n", response.replace(/\r\n|\r/,"\n"))
            t.assertEqual('simple ajax load', testEl.html().trim())
            t.assertEqual('success', status)
            t.assertIn('abort', xhr)
            t.assertEqualList('ajaxBeforeSend ajaxSend success ajaxSuccess ajaxComplete', t.reg.events())
          })
        )
        t.assertIdentical(testEl, el)
      },

      testAjaxLoadWithSelector: function(t) {
        t.pause()
        var testEl = $('#ajax_load')
        testEl.load(
          'fixtures/ajax_load_selector.html #ajax_load_test_div',
          t.reg.resumeHandler('success', function(){
            t.assertEqual(
              '<div id="ajax_load_test_div">ajax load with selector</div>',
              testEl.html().trim()
            )
          })
        )
      },

      testAjaxLoadWithJavaScript: function (t) {
        var testEl = $('#ajax_load')
        t.pause()
        window.testValue = 0
        testEl.load('fixtures/ajax_load_selector_javascript.html', function(){
          deferredResume(t, function(){
            this.assertEqual(window.testValue, 1)
            delete window.testValue
          })
        })
      },

      testAjaxLoadWithSelectorAndJavaScript: function (t) {
        var testEl = $('#ajax_load')
        t.pause()
        window.testValue = 0
        testEl.load('fixtures/ajax_load_selector_javascript.html #ajax_load_test_div', function() {
          deferredResume(t, function(){
            this.assertEqual(window.testValue, 0)
            delete window.testValue
          })
        })
      },

      testAjaxWithContext: function(t) {
        t.pause()
        var body = $('body')
        $.ajax({
          url:        'fixtures/ajax_load_simple.html',
          context:    body,
          beforeSend: t.reg.handler('beforeSend'),
          success:    t.reg.handler('success'),
          complete:   t.reg.resumeHandler('complete', function(){
            t.assertIdentical(body, this)
            t.assertIdentical(body, t.reg.context('beforeSend'))
            t.assertIdentical(body, t.reg.context('success'))
            t.assertIdentical(document.body, t.reg.target('ajaxBeforeSend'))
            t.assertIdentical(document.body, t.reg.target('ajaxSuccess'))
          })
        })
      },

      testAjaxWithContextPlainObject: function(t) {
        t.pause()
        var obj = {}
        $.ajax({
          url:        'fixtures/ajax_load_simple.html',
          context:    obj,
          beforeSend: t.reg.handler('beforeSend'),
          success:    t.reg.handler('success'),
          complete:   t.reg.resumeHandler('complete', function(){
            t.assertIdentical(obj, this)
            t.assertIdentical(obj, t.reg.context('beforeSend'))
            t.assertIdentical(obj, t.reg.context('success'))
            t.assertUndefined(t.reg.target('ajaxBeforeSend'))
            t.assertUndefined(t.reg.target('ajaxSuccess'))
          })
        })
      },

      testAjaxLoadJavaScript: function (t) {
        t.pause()
        window.testValue = 0
        var xhr = $.ajax({
          url:'fixtures/ajax_load_javascript.js',
          success: t.reg.resumeHandler('success', function(response){
            // check that the javascript has loaded and executed.
            t.assertEqual(window.testValue, 1)
            delete window.testValue
          })
        })
      },

      testAjaxBasicAuth: function(t) {
        t.pause()
        resumeOnAjaxError(t)
        $.ajax({
          url: 'auth?realm=zepto',
          username: 'zepto',
          password: 'doge',
          success: t.reg.resumeHandler('success')
        })
      }
    })

    var OriginalXHR = $.ajaxSettings.xhr

    function MockXHR() {
      this.headers = []
      this.responseHeaders = {}
      MockXHR.last = this
    }
    MockXHR.prototype = {
      open: function(method, url, async) {
        this.method = method
        this.url = url
        this.async = async
      },
      setRequestHeader: function(name, value) {
        if (!this.method) throw "setRequestHeader() called before open()"
        this.headers.push({ name: name, value: value })
      },
      getResponseHeader: function(name) {
        return this.responseHeaders[name]
      },
      overrideMimeType: function(type) {
        this.responseHeaders['content-type'] = type
      },
      withCredentials: false,
      send: function(data) {
        this.data = data
      },
      abort: function() {
        this.aborted = true
      },
      ready: function(readyState, status, responseText, headers) {
        this.readyState = readyState
        this.status = status
        this.responseText = responseText
        $.extend(this.responseHeaders, headers)
        this.onreadystatechange()
      },
      onreadystatechange: function() {}
    }

    function matchHeader(name, value) {
      return function(header) {
        return header.name == name && (!value || header.value == value)
      }
    }

    Evidence('ZeptoAjaxTest2', {
      setUp: function() {
        $.ajaxSettings.xhr = function(){ return new MockXHR }
      },

      tearDown: function() {
        $.ajaxSettings.xhr = OriginalXHR
        $(document).off()
        $.active = 0
      },

      testTypeDefaultsToGET: function(t) {
        $.ajax({
          url: '/foo',
          beforeSend: function(xhr, settings) {
            t.assertFalse(settings.crossDomain)
          }
        })
        t.assertEqual('GET', MockXHR.last.method)
      },

      testURLDefaultsToWindowLocation: function(t) {
        $.ajax()
        t.assertEqual(window.location, MockXHR.last.url)
      },

      testCrossDomainCrossOrigin: function(t) {
        $.ajax({
          url: 'http://example.com/foo',
          beforeSend: function(xhr, settings) {
            t.assertTrue(settings.crossDomain)
          }
        })
      },

      testCrossDomainSameOrigin: function(t) {
        $.ajax({
          url: window.location.href,
          beforeSend: function(xhr, settings) {
            t.assertFalse(settings.crossDomain)
          }
        })
      },

      testCrossDomainLeadingSpace: function(t) {
        $.ajax({
          url: ' http://example.com/foo',
          beforeSend: function(xhr, settings) {
            t.assertTrue(settings.crossDomain)
          }
        })
      },

      testCrossDomainTrippleSlash: function(t) {
        $.ajax({
          url: 'http:///example.com/foo',
          beforeSend: function(xhr, settings) {
            t.assertTrue(settings.crossDomain)
          }
        })
      },

      testCrossDomainWithQuery: function(t) {
        $.ajax({
          url: window.location.href + "?foo=bar",
          beforeSend: function(xhr, settings) {
            t.assertFalse(settings.crossDomain)
          }
        })
      },

      testCrossDomainDifferentScheme: function(t) {
        $.ajax({
          url: "foo://" + window.location.host,
          beforeSend: function(xhr, settings) {
            t.assertTrue(settings.crossDomain)
          }
        })
      },

      testCrossDomainPeriodInScheme: function(t) {
        $.ajax({
          url: "foo.bar://example.com/foo",
          beforeSend: function(xhr, settings) {
            t.assertTrue(settings.crossDomain)
          }
        })
      },

      testDefaultAcceptHeader: function(t) {
        $.ajax()
        t.assert(MockXHR.last.headers.some(matchHeader('Accept', '*/*')))
      },

      testDataTypeOptionSetsAcceptHeader: function(t) {
        $.ajax({ dataType: 'json' })
        t.assert(MockXHR.last.headers.some(matchHeader('Accept', 'application/json')))
        // verifies overrideMimeType:
        t.assertEqual('application/json', MockXHR.last.getResponseHeader('content-type'))

        $.ajax({ dataType: 'script' })
        //check that the head matches all of those required for loading scripts.
        t.assert(MockXHR.last.headers.some(matchHeader('Accept', 'text/javascript, application/javascript, application/x-javascript')))
      },

      testMimeTypeOptionOverride: function(t) {
        $.ajax({ mimeType: 'text/plain' })
        t.assertEqual('text/plain', MockXHR.last.getResponseHeader('content-type'))
      },

      testContentTypeOptionSetsContentTypeHeader: function(t) {
        $.ajax()
        t.assert(!MockXHR.last.headers.some(matchHeader('Content-Type')))

        $.ajax({ contentType: 'text/html' })
        t.assert(MockXHR.last.headers.some(matchHeader('Content-Type', 'text/html')))

        $.ajax({ type: 'POST', data: [], contentType: 'application/x-foo' })
        t.assert(MockXHR.last.headers.some(matchHeader('Content-Type', 'application/x-foo')))
      },

      testHeadersOptionCanSetContentType: function(t) {
        $.ajax({ type: 'POST', data: [], headers: { 'Content-Type': 'application/hal+json' }})
        t.assert(MockXHR.last.headers.some(matchHeader('Content-Type', 'application/hal+json')))
      },

      testContentTypeDefaultsToUrlEncoded: function(t) {
        $.ajax({ type: 'GET', data: 'x' })
        t.refute(MockXHR.last.headers.some(matchHeader('Content-Type', 'application/x-www-form-urlencoded')))
        $.ajax({ type: 'POST', data: 'x' })
        t.assert(MockXHR.last.headers.some(matchHeader('Content-Type', 'application/x-www-form-urlencoded')))
      },

      testDefaultContentTypeDisabled: function(t) {
        $.ajax({ type: 'POST', data: {a:1}, contentType: false })
        t.refute(MockXHR.last.headers.some(matchHeader('Content-Type')))
      },

      testCustomHeader: function(t) {
        $.ajax({ headers: {'X-Awesome': 'true'} })
        t.assert(MockXHR.last.headers.some(matchHeader('X-Requested-With', 'XMLHttpRequest')))
        t.assert(MockXHR.last.headers.some(matchHeader('X-Awesome', 'true')))
      },

      testXhrFieldsOptionSetsWithCredentialsProperty: function(t) {
        var xhr = $.ajax({
          url: 'http://example.com/foo',
          xhrFields: { withCredentials: true }
        })
        t.assert(xhr.withCredentials)
      },

      testScriptDataTypeCacheBustByDefault: function(t) {
        $.ajax({ url: '/', dataType: 'script' })
        var xhr = MockXHR.last
        t.assert(/^\/\?_=[\d]{13}$/.test(xhr.url), xhr.url)
      },

      testScriptDataTypeCacheBustDisable: function(t) {
        $.ajax({ url: '/', dataType: 'script', cache: true })
        var xhr = MockXHR.last
        t.assertEqual('/', xhr.url)
      },

      testJSONdataType: function(t) {
        var result = {}
        $.ajax({ dataType: 'json', success: function(json) {result = json } })
        MockXHR.last.ready(4, 200, '{"hello":"world"}')
        t.assertEqual("world", result.hello)
      },

      testJSONcontentType: function(t) {
        var result = {}
        $.ajax({ success: function(json) {result = json } })
        MockXHR.last.ready(4, 200, '{"hello":"world"}', {'content-type': 'application/json'})
        t.assertEqual("world", result.hello)
      },

      testJSONResponseBodiesAreNotParsedWhenDataTypeOptionIsJSONButResponseIsEmptyString: function(t) {
        var result, success = false
        $.ajax({ dataType: 'json', success: function(json) {result = json; success = true } })
        MockXHR.last.ready(4, 200, '')
        t.assert(success)
        t.assertNull(result)
      },

      testJSONResponseBodiesAreNotParsedWhenDataTypeOptionIsJSONButResponseIsSingleSpace: function(t) {
        var result, success = false
        $.ajax({ dataType: 'json', success: function(json) {result = json; success = true } })
        MockXHR.last.ready(4, 200, ' ')
        t.assert(success)
        t.assertNull(result)
      },

      testDataIsNotSentAsBodyForGetRequests: function(t) {
        $.ajax({ type: 'GET', data: {a:'b'} })
        t.assertNull(MockXHR.last.data)
      },

      testDataOptionIsConvertedToSerializedForm: function(t) {
        $.ajax({
          type: 'POST',
          data: {hello: 'world', array: [1,2,3], object: { prop1: 'val', prop2: 2 } }
        })
        var data = decodeURIComponent(MockXHR.last.data)
        t.assertEqual('hello=world&array[]=1&array[]=2&array[]=3&object[prop1]=val&object[prop2]=2', data)
      },

      testDataOptionIsConvertedToSerializedTraditionalForm: function(t) {
        $.ajax({
          type: 'POST',
          data: {hello: 'world', array: [1,2,3], object: { prop1: 'val', prop2: 2 } },
          traditional: true
        })
        var data = decodeURIComponent(MockXHR.last.data)
        t.assertEqual('hello=world&array=1&array=2&array=3&object=[object+Object]', data)
      },

      testQueryParamsTraditional: function(t) {
        $.ajax({
          url: '/',
          data: {hello: 'world', array: [1,2,3], object: { prop1: 'val', prop2: 2 } },
          traditional: true
        })
        var url = decodeURIComponent(MockXHR.last.url)
        t.assertEqual('/?hello=world&array=1&array=2&array=3&object=[object+Object]', url)
      },

      testProcessDataDisabled: function(t) {
        var data = { country: 'Ecuador' }
        $.ajax({
          data: data,
          processData: false,
          type: "POST"
        })
        t.assertIdentical(data, MockXHR.last.data)
      },

      testDataIsAppendedToGETURL: function(t) {
        $.ajax({ url:'test.html', data:'foo=bar' })
        t.assertEqual('test.html?foo=bar', MockXHR.last.url)

        $.ajax({ url:'test.html', data:'?foo=bar' })
        t.assertEqual('test.html?foo=bar', MockXHR.last.url)

        $.ajax({ url:'test.html?', data:'foo=bar' })
        t.assertEqual('test.html?foo=bar', MockXHR.last.url)

        $.ajax({ url:'test.html?baz', data:'foo=bar' })
        t.assertEqual('test.html?baz&foo=bar', MockXHR.last.url)

        $.ajax({ url:'test.html?bar=baz', data:'foo=bar' })
        t.assertEqual('test.html?bar=baz&foo=bar', MockXHR.last.url)

        $.ajax({ url:'test.html', data:{foo:'bar'} })
        t.assertEqual('test.html?foo=bar', MockXHR.last.url)
      },

      testDataIsAppendedToGETURLWithHashStripped: function(t) {
        $.ajax({ url:'test.html#?ignore=me', data:'foo=bar' })
        t.assertEqual('test.html?foo=bar', MockXHR.last.url)

        $.ajax({ url:'test.html?a=b#?ignore=me', data:'foo=bar' })
        t.assertEqual('test.html?a=b&foo=bar', MockXHR.last.url)
      },

      testScriptResponseIsEvald: function(t) {
        var result
        $.ajax({ success: function(text){ result = text } })
        MockXHR.last.ready(4, 200, 'this.testValue = 42', {'content-type': 'application/javascript'})
        t.assertEqual('this.testValue = 42', result)
        t.assertIdentical(42, window.testValue)
        delete window.testValue
      },

      testErrorCallback: function(t) {
        var successFired = false, xhr, status
        $.ajax({
          success: function() { successFired = true },
          error: function(x, s) { xhr = x, status = s }
        })

        MockXHR.last.ready(4, 500, '500 Internal Server Error')
        t.assert(!successFired)
        t.assertEqual(MockXHR.last, xhr)
        t.assertEqual('error', status)
      },

      testErrorCallbackWithInvalidJSON: function(t) {
        var successFired = false, xhr, status, exception
        $.ajax({
          dataType: 'json',
          succes: function() { successFired = true },
          error: function(x, s, e) { xhr = x, status = s, exception = e }
        })

        MockXHR.last.ready(4, 200, '{invalid')
        t.assert(!successFired)
        t.assertEqual(MockXHR.last, xhr)
        t.assertEqual('parsererror', status)
        t.assert(exception.toString().match(/SyntaxError/))
      },

      test201ResponseIsSuccess: function(t) {
        var successFired, errorFired
        $.ajax({
          success: function() { successFired = true },
          error: function() { errorFired = true }
        })

        MockXHR.last.ready(4, 201, 'Created')
        t.assert(successFired)
        t.refute(errorFired)
      },

      test304ResponseIsSuccess: function(t) {
        var successFired, errorFired
        $.ajax({
          success: function() { successFired = true },
          error: function() { errorFired = true }
        })

        MockXHR.last.ready(4, 304, 'Not Modified')
        t.assert(successFired)
        t.refute(errorFired)
      },
      testXHRParameterInSuccessCallback: function(t) {
        var body, status, xhr
        $.ajax({
          success: function(b, s, x) { body = b, status = s, xhr = x }
        })

        MockXHR.last.ready(4, 200, 'Hello')
        t.assertEqual('Hello', body)
        t.assertEqual('success', status)
        t.assertEqual(MockXHR.last, xhr)

        body = status = xhr = null
        $.ajax({
          dataType: 'json',
          success: function(b, s, x) { body = b, status = s, xhr = x }
        })

        MockXHR.last.ready(4, 200, '{"message":"Hello"}')
        t.assertEqual('Hello', body.message)
        t.assertEqual('success', status)
        t.assertEqual(MockXHR.last, xhr)
      },

      testBeforeSendCanChangeUrl: function(t) {
        var xhr = $.ajax({
          url: 'one',
          beforeSend: function(x, s) {
            s.url = 'two'
          }
        })
        t.assertEqual('two', xhr.url)
      },

      testBeforeSendCanChangeMethod: function(t) {
        var xhr = $.ajax({
          type: 'POST',
          beforeSend: function(x, s) {
            s.type = 'PUT'
          }
        })
        t.assertEqual('PUT', xhr.method)
      },

      testBeforeSendCanAccessSerializedPostData: function(t) {
        var xhr = $.ajax({
          type: 'POST',
          data: { a:'b' },
          beforeSend: function(x, s) {
            s.data = s.data.toUpperCase()
          }
        })
        t.assertEqual('A=B', xhr.data)
      },

      testBeforeSendCannotAccessRawQueryParams: function(t) {
        var url, data
        $.ajax({
          url: '/',
          type: 'GET',
          data: { a:'b' },
          beforeSend: function(x, s) {
            url = s.url
            data = s.data
          }
        })
        t.assertEqual('/?a=b', url)
        t.assertUndefined(data)
      },

      testBeforeSendCannotChangeOrAddHeadersViaObject: function(t) {
        var xhr = $.ajax({
          headers: { a:'b', c:'d' },
          beforeSend: function(x, s) {
            s.headers['a'] = 'B'
            s.headers['e'] = 'f'
          }
        })
        t.assert(xhr.headers.some(matchHeader('a', 'b')))
        t.assert(xhr.headers.some(matchHeader('c', 'd')))
        t.refute(xhr.headers.some(matchHeader('e', 'f')))
      },

      testBeforeSendCanChangeAndAddHeadersViaXhr: function(t) {
        var xhr = $.ajax({
          headers: { a:'b', c:'d' },
          beforeSend: function(x, s) {
            x.setRequestHeader('a', 'B')
            x.setRequestHeader('e', 'f')
          }
        })
        t.assert(xhr.headers.some(matchHeader('a', 'B')))
        t.assert(xhr.headers.some(matchHeader('c', 'd')))
        t.assert(xhr.headers.some(matchHeader('e', 'f')))
      },

      testBeforeSendAbortCallback: function(t) {
        var xhr, settings
        $.ajax({
          beforeSend: function(x, s) {
            settings = s, xhr = x
          }
        })

        t.assertEqual(MockXHR.last, xhr)
        t.assertEqual('GET', settings.type)
      },

      testBeforeSendAbort: function(t) {
        var xhr
        $.ajax({ beforeSend: function(x) { xhr = x; return false } })

        t.assert(xhr.aborted)
      },

      testGlobalBeforeSendAbort: function(t) {
        $(document).on('ajaxBeforeSend', false)
        var xhr = $.ajax()
        t.assert(xhr.aborted)
      },

      testGlobalAjaxSendCantAbort: function(t) {
        var xhr
        $(document).on('ajaxSend', function(e, x) { xhr = x; return false })
        t.assert($.ajax())
        t.assert(!xhr.aborted)
      },

      testCompleteCallback: function(t) {
        var status, xhr
        $.ajax({ complete: function(x, s) { status = s, xhr = x } })

        MockXHR.last.ready(4, 200, 'OK')
        t.assertEqual(MockXHR.last, xhr)
        t.assertEqual('success', status)
      },

      testCallbackOrder: function(t) {
        var order = []
        $.ajax({
          beforeSend: function() { order.push('beforeSend') },
          success: function() { order.push('success') },
          complete: function() { order.push('complete') }
        })

        MockXHR.last.ready(4, 200, 'OK')
        t.assertEqual('beforeSend,success,complete', order.join(','))
      },

      testGlobalCallbacks: function(t) {
        var fired = []
        $(document).on('ajaxStart ajaxBeforeSend ajaxSend ajaxSuccess ajaxError ajaxComplete ajaxStop', function(e) {
          fired.push(e.type)
        })

        $.ajax({
          beforeSend: function() { fired.push('beforeSend') },
          success:    function() { fired.push('success') },
          error:      function() { fired.push('error') },
          complete:   function() { fired.push('complete') }
        })
        t.assertEqual('ajaxStart beforeSend ajaxBeforeSend ajaxSend', fired.join(' '))

        fired = []
        MockXHR.last.ready(4, 200, 'OK')
        t.assertEqual('success ajaxSuccess complete ajaxComplete ajaxStop', fired.join(' '))
      },

      testGlobalCallbacksOff: function(t) {
        var fired = []
        $(document).on('ajaxStart ajaxBeforeSend ajaxSend ajaxSuccess ajaxError ajaxComplete ajaxStop', function(e) {
          fired.push(e.type)
        })

        $.ajax({
          global:     false,
          beforeSend: function() { fired.push('beforeSend') },
          success:    function() { fired.push('success') },
          error:      function() { fired.push('error') },
          complete:   function() { fired.push('complete') }
        })
        t.assertEqual('beforeSend', fired.join(' '))

        fired = []
        MockXHR.last.ready(4, 200, 'OK')
        t.assertEqual('success complete', fired.join(' '))
      },

      testTimeout: function(t) {
        var successFired = false, xhr, status, globalStatus
        t.pause()
        $(document).on('ajaxError', function (e, x, o, s) {
          globalStatus = s
        })

        $.ajax({
          timeout: 30,
          success: function() { successFired = true },
          error: function(x, s) { xhr = x, status = s }
        })

        setTimeout(function(){
          t.assertFalse(successFired)
          t.assertUndefined(status)
        }, 20)

        setTimeout(function(){
          t.resume(function(){
            t.assertFalse(successFired)
            t.assertTrue(xhr.aborted)
            t.assertEqual('timeout', status)
            t.assertEqual('timeout', globalStatus)
          })
        }, 40)
      },

      testAbort: function(t) {
        var successFired = false, xhr, status, globalStatus
        t.pause()
        $(document).on('ajaxError', function (e, x, o, s) {
          globalStatus = s
        })

        $.ajax({
          success: function() { successFired = true },
          error: function(x, s) { xhr = x, status = s }
        })
        MockXHR.last.abort()
        MockXHR.last.ready(4, 0, 'Aborted')

        setTimeout(function(){
          t.resume(function(){
            t.assertFalse(successFired)
            t.assertTrue(xhr.aborted)
            t.assertEqual('abort', status)
            t.assertEqual('abort', globalStatus)
          })
        }, 20)
      },

      testAsyncDefaultsToTrue: function(t) {
        $.ajax({ url: '/foo' })
        t.assertTrue(MockXHR.last.async)
      },

      testAsyncFalse: function(t) {
        $.ajax({ url: '/foo', async: false })
        t.assertFalse(MockXHR.last.async)
      }
    })

    Evidence('ZeptoAjaxHelperMethodsTest', {

      testParamMethod: function(t) {
        var result = $.param({ libs: ['jQuery', 'script.aculo.us', 'Prototype', 'Dojo'] })
        result = decodeURIComponent(result)
        t.assertEqual(result, "libs[]=jQuery&libs[]=script.aculo.us&libs[]=Prototype&libs[]=Dojo")

        result = $.param({ jquery: 'Javascript', rails: 'Ruby', django: 'Python' })
        result = decodeURIComponent(result)
        t.assertEqual(result, "jquery=Javascript&rails=Ruby&django=Python")

        result = $.param({
          title: "Some Countries",
          list: ['Ecuador', 'Austria', 'England'],
          capitals: { ecuador: 'Quito', austria: 'Vienna', GB: { england: 'London', scotland: 'Edinburgh'} }
        })
        result = decodeURIComponent(result)
        t.assertEqual(result, "title=Some+Countries&list[]=Ecuador&list[]=Austria&list[]=England&capitals[ecuador]=Quito&capitals[austria]=Vienna&capitals[GB][england]=London&capitals[GB][scotland]=Edinburgh")
      },

      testParamNullValues: function(t) {
        t.assertEqual("x=&a[b]=&a[c]=",
          decodeURIComponent($.param({ x: null, a: { b: null, c: undefined }})))
        t.assertEqual("x=",
          decodeURIComponent($.param([{ name: "x", value: null }])))
      },

      testParamFunctionValues: function(t) {
        t.assertEqual("x=&a[b]=B",
          decodeURIComponent($.param({ x: function(){}, a: { b: function(){ return "B" }}})))
        t.assertEqual("x=&a[b]=B",
          decodeURIComponent($.param([{ name: "x", value: null }, { name: "a[b]", value: function(){ return "B" }}])))
      },

      testParamEscaping: function(t) {
        var result = $.param({ 'equation[1]': 'bananas+peaches=smoothie' })
        t.assertEqual("equation%5B1%5D=bananas%2Bpeaches%3Dsmoothie", result)
      },

      testParamSpaces: function(t) {
        var result = $.param({ "foo bar": "baz kuux" })
        t.assertEqual("foo+bar=baz+kuux", result)
      },

      testParamComplex: function(t) {
        var data = {
          a: ['b', 'c', { d:'e', f:['g','h'] }]
        }
        var result = $.param(data)
        result = decodeURIComponent(result)
        t.assertEqual("a[]=b&a[]=c&a[2][d]=e&a[2][f][]=g&a[2][f][]=h", result)
      },

      testParamComplexIssue850: function(t) {
        var data = {
          $push: { _:[ { name: 'bob', members:['aaa'] } ] }
        }
        var result = $.param(data)
        result = decodeURIComponent(result)
        t.assertEqual("$push[_][0][name]=bob&$push[_][0][members][]=aaa", result)
      },

      testParamComplexNested: function(t) {
        var data = {
          x: [
            [1, { attr1: 2 } ],
            3,
            { attr1: 4 },
            { attr1: 5, attr2: [ 6 ] },
            [ 7, [8, 9] ],
            { attr1: { attr2: [ [ 10 ] ] } }
          ]
        }
        var result = $.param(data)
        result = decodeURIComponent(result)
        t.assertEqual(
          [ "x[0][]=1"
          , "x[0][1][attr1]=2"
          , "x[]=3"
          , "x[2][attr1]=4"
          , "x[3][attr1]=5"
          , "x[3][attr2][]=6"
          , "x[4][]=7"
          , "x[4][1][]=8"
          , "x[4][1][]=9"
          , "x[5][attr1][attr2][0][]=10"
          ].join('&'), result)
      },

      testParamShallow: function(t) {
        var data = {
          libs: ['jQuery', 'Prototype', 'Dojo'],
          nested: { will: 'be ignored' }
        }
        var result = $.param(data, true)
        result = decodeURIComponent(result)
        t.assertEqual("libs=jQuery&libs=Prototype&libs=Dojo&nested=[object+Object]", result)
      },

      testParamArray: function(t) {
        var data = [
          {name:'country', value:'Ecuador'},
          {name:'capital', value:'Quito'}
        ]
        var result = $.param(data)
        t.assertEqual(result, "country=Ecuador&capital=Quito")
      }

    })
  })()
  </script>
</body>
</html>
